R Notebook

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.2 ──✔ ggplot2 3.3.6      ✔ purrr   0.3.4 
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.2.1      ✔ stringr 1.4.0 
✔ readr   2.1.2      ✔ forcats 0.5.2 ── Conflicts ───────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(geojsonio)
Registered S3 method overwritten by 'geojsonsf':
  method        from   
  print.geojson geojson

Attaching package: ‘geojsonio’

The following object is masked from ‘package:base’:

    pretty
library(broom)
library(sf)
Linking to GEOS 3.10.2, GDAL 3.4.2, PROJ 8.2.1; sf_use_s2() is TRUE
library(osmdata)
Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
library(ggplot2)
library(dplyr)
library(ggnewscale)
library(RSocrata)
library(rstudioapi)
library(leaflet)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(shiny)

Attaching package: ‘shiny’

The following object is masked from ‘package:geojsonio’:

    validate
library(bslib)

Attaching package: ‘bslib’

The following object is masked from ‘package:broom’:

    bootstrap

The following object is masked from ‘package:utils’:

    page
library(RColorBrewer)
#set working directory
current_path <- rstudioapi::getActiveDocumentContext()$path
setwd(dirname(current_path))
getwd()
[1] "/Users/xxxxoxygene/Downloads/Columbia University/Fall2022/STAT4243/Project 2/fall2022-project2-group2/doc"
#### Load the Restaurant dataset
data =  read.socrata(
  "https://data.cityofnewyork.us/resource/43nn-pn8j.json",
  app_token = "zTRehp1897SQtpYtBiIOUMfR4"
)

data$year <- format(data$inspection_date,"%Y") ## Extract Years

###filter the dataset
df =
  data %>%
  filter(data$year >= 2019 & zipcode!="" & dba != "") %>%
  mutate(grade=replace(grade, grade == "", NA))

head(df)
# Read the geojson file containing Spatial Info
spdf_file <- geojson_read("../data/zip_code_040114.geojson", what = "sp")

stats_df = spdf_file@data

# Convert it to a spatial data frame, with zip code as index
spdf_data <- tidy(  spdf_file,
  region="ZIPCODE"  # Use ZIPCODE variable as index, the index will be named "id"
)

Section II: interactive map


##### None Interactive map (Population by region)
ggplot() +
  geom_polygon(data=spdf_data %>%
                 left_join(stats_df, c("id"="ZIPCODE")),
               aes(x=long,
                   y=lat,
                   group=group,
                   fill=POPULATION),
               color="white",
               size=.2) +
  theme_void() +
  coord_map() +
  scale_fill_distiller(palette = "YlGnBu", direction = 1) +
  labs(title="Population in New York City",
       subtitle="Neighborhoods are filled by population",
       fill="Population")


#### Number of restaurant per ZIPCODE
Num_Rest_Code =
  df%>%
  group_by(zipcode, dba, latitude, longitude)%>%
  count() %>%
  group_by(zipcode)%>%
  count()

Critical_2019_by_Code = 
  df%>%
  filter(year == 2019)%>%
  group_by(zipcode)%>%
  summarize(Total = n())
  
Critical_2020_by_Code = 
  df%>%
  filter(year == 2020)%>%
  group_by(zipcode)%>%
  summarize(Total = n())
  
Critical_2021_by_Code = 
  df%>%
  filter(year == 2021)%>%
  group_by(zipcode)%>%
  summarize(Total = n())
  
Critical_2022_by_Code = 
  df%>%
  filter(year == 2022)%>%
  group_by(zipcode)%>%
  summarize(Total = n())


Critical_spdf_file_2022 = spdf_file
Critical_spdf_file_2022@data =
Critical_spdf_file_2022@data %>%
                 left_join(Critical_2022_by_Code, c("ZIPCODE"="zipcode"))



Critical_spdf_file_2019 = spdf_file
Critical_spdf_file_2019@data =
Critical_spdf_file_2019@data %>%
                 left_join(Critical_2019_by_Code, c("ZIPCODE"="zipcode"))


Critical_spdf_file_2020 = spdf_file
Critical_spdf_file_2020@data =
Critical_spdf_file_2020@data %>%
                 left_join(Critical_2020_by_Code, c("ZIPCODE"="zipcode"))

Critical_spdf_file_2021 = spdf_file
Critical_spdf_file_2021@data =
Critical_spdf_file_2021@data %>%
                 left_join(Critical_2021_by_Code, c("ZIPCODE"="zipcode"))


critical_violations = list(Critical_spdf_file_2019,Critical_spdf_file_2020,Critical_spdf_file_2021,Critical_spdf_file_2022)
names(critical_violations) <- c("2019","2020","2021","2022")



nc_pal= colorNumeric(palette="YlOrBr", domain= Critical_spdf_file_2019@data$Total, na.color = 'transparent')



leaflet()%>%
  addProviderTiles("CartoDB")%>%
  #### First Layer of PolyGons
  addPolygons(
    data = Critical_spdf_file_2022 ,
    weight = 0.5,
    color = "black",
    stroke=TRUE ,
    opacity = 1 ,
    fillColor = ~nc_pal(Total),
    label = ~paste0 ('Total Critical Violation : ' , Total),
    group = '2022',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
    ) %>%
  
  #### Second Layer of PolyGons
    addPolygons(
    data = Critical_spdf_file_2021 ,
    weight = 0.5,
    color = "black",
    stroke=TRUE ,
    opacity = 1 ,
    fillColor = ~nc_pal(Total),
    
    label =~paste0 ('Total Critical Violation : ' , Total),
    group = '2021',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
    ) %>%
    addLayersControl(overlayGroups = c("2022", "2021"))%>%
  
  
  
    addLegend( pal=nc_pal, values= Critical_spdf_file_2022$Total, opacity=0.9, title = "Critical", position = "bottomleft" )
Warning: Some values were outside the color scale and will be treated as NAWarning: Some values were outside the color scale and will be treated as NA
NA



Total_violation = 
  df%>%
  group_by(zipcode)%>%
  summarize(Total = n())


Total_2019_by_Code = 
  df%>%
  filter(year == 2019)%>%
  group_by(zipcode)%>%
  summarize(Total = n())
  
Total_2020_by_Code = 
  df%>%
  filter(year == 2020)%>%
  group_by(zipcode)%>%
  summarize(Total = n())
  
Total_2021_by_Code = 
  df%>%
  filter(year == 2021)%>%
  group_by(zipcode)%>%
  summarize(Total = n())
  
Total_2022_by_Code = 
  df%>%
  filter(year == 2022)%>%
  group_by(zipcode)%>%
  summarize(Total = n())





##### Join datasets
Total_spdf_file_2022 = spdf_file
Total_spdf_file_2022@data =
Total_spdf_file_2022@data %>%
                 left_join(Total_2022_by_Code, c("ZIPCODE"="zipcode"))



Total_spdf_file_2019 = spdf_file
Total_spdf_file_2019@data =
Total_spdf_file_2019@data %>%
                 left_join(Total_2019_by_Code, c("ZIPCODE"="zipcode"))


Total_spdf_file_2020 = spdf_file
Total_spdf_file_2020@data =
Total_spdf_file_2020@data %>%
                 left_join(Total_2020_by_Code, c("ZIPCODE"="zipcode"))

Total_spdf_file_2021 = spdf_file
Total_spdf_file_2021@data =
Total_spdf_file_2021@data %>%
                 left_join(Total_2021_by_Code, c("ZIPCODE"="zipcode"))


total_violation <- list(Total_spdf_file_2019,Total_spdf_file_2020,Total_spdf_file_2021,Total_spdf_file_2022)
names(total_violation) <- c("2019","2020","2021","2022")


##### colors
nc_pal= colorNumeric(palette="YlOrBr", domain= Total_spdf_file_2019@data$Total, na.color = 'transparent')


leaflet()%>%
  addProviderTiles("CartoDB")%>%
  #### First Layer of PolyGons
  addPolygons(
    data = Total_spdf_file_2022 ,
    weight = 0.5,
    color = "black",
    stroke=TRUE ,
    opacity = 1 ,
    fillColor = ~nc_pal(Total),
    label = ~paste0 ('Total Critical Violation : ' , Total),
    group = '2022',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
    ) %>%
  
  #### Second Layer of PolyGons
    addPolygons(
    data = Total_spdf_file_2021 ,
    weight = 0.5,
    color = "black",
    stroke=TRUE ,
    opacity = 1 ,
    fillColor = ~nc_pal(Total),
    label =~paste0 ('Total  Violation : ' , Total),
    group = '2021',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
    ) %>%
    addLayersControl(overlayGroups = c("2022", "2021"))%>%
  
  #####Third layer
    addPolygons(
    data = Total_spdf_file_2020 ,
    weight = 0.5,
    color = "black",
    stroke=TRUE ,
    opacity = 1 ,
    fillColor = ~nc_pal(Total),
    
    label =~paste0 ('Total  Violation : ' , Total),
    group = '2020',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
    ) %>%
  
  ####Fourth
    addPolygons(
    data = Total_spdf_file_2019 ,
    weight = 0.5,
    color = "black",
    stroke=TRUE ,
    opacity = 1 ,
    fillColor = ~nc_pal(Total),
    label =~paste0 ('Total  Violation : ' , Total),
    group = '2019',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
    ) %>%
   
  
    addLayersControl(overlayGroups = c("2022", "2021",'2020', '2019'))%>%
    addLegend( pal=nc_pal, values= Total_spdf_file_2022$Total, opacity=0.9, title = "Count of Total Violation", position = "bottomleft" )
Warning: Some values were outside the color scale and will be treated as NAWarning: Some values were outside the color scale and will be treated as NA
violations <- list(total_violation,critical_violations)
names(violations) <- c("Number of Total Violations","Number of Crital Violations")

restaurant info

for (y in c("2019","2020","2021","2022")){
  score_year[y] <- score[score$year==y,]
}
Warning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement length

Section III: Rshiny App

nc_pal= colorNumeric(palette="YlOrBr", domain= Total_spdf_file_2022@data$Total,na.color = 'transparent')
ui <- navbarPage(
 theme = bs_theme(bootswatch = "litera"),
  "Food Inspectation",
  tabPanel("Introduction"),
  tabPanel("Static Plots"),
 
  navbarMenu("Interactive Plots",
             tabPanel("Interactive Map",
           fluidRow(column(6,selectInput("type","Type of Violations:",
                                    c("Number of Total Violations",
                                      "Number of Crital Violations"))),
                    column(6,selectInput("time","Year:",
                                    c("2019","2020","2021","2022")))),
          fluidRow(leafletOutput("map",height = 1000))),
          
          
            tabPanel("Comparison between Years",
                      fluidRow(column(4,selectInput("type_comp","Type of Violations:",
                                    c("Number of Total Violations",
                                      "Number of Crital Violations"))),
                    column(4,selectInput("time1","Year:",
                                    c("2019","2020","2021","2022"))),
                    column(4,selectInput("time2","Year:",
                                    c("2019","2020","2021","2022"),selected = "2020"))),
                    fluidRow(column(6,leafletOutput("map_comp1",height=600)), column(6,leafletOutput("map_comp2",height=600))))),
 
  tabPanel("Reference")
)
server <- function(input, output,session){
  #interactive map
  output$map <- renderLeaflet({
    leaflet()%>%
  addProviderTiles("CartoDB")%>%
  #### First Layer of PolyGons
  addPolygons(
    data = violations[[input$type]][[input$time]],
    weight = 0.5,
    color = "black",
    stroke=TRUE ,
    opacity = 1 ,
    fillColor = ~nc_pal(Total),
    label = ~paste0 ('Total Violations : ' , Total),
    group = '2022',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
    )%>%
    addLegend( pal=nc_pal, values= violations[[input$type]][[input$time]]$Total, opacity=0.9, title = "Count of Total Violation", position = "bottomleft" )
    })
  
  
  
  
#interactive map compared by year  
    output$map_comp1 <- renderLeaflet({
      leaflet()%>%
      addProviderTiles("CartoDB")%>%
      addPolygons(
      data = violations[[input$type_comp]][[input$time1]],
      weight = 0.5,
      color = "black",
      stroke=TRUE ,
      opacity = 1 ,
      fillColor = ~nc_pal(Total),
      label = ~paste0 ('Total Violations : ' , Total),
      group = '2022',
      highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
      )%>%
      addLegend( pal=nc_pal, values= violations[[input$type]][[input$time1]]$Total, opacity=0.9, title = "Count of Total Violation", position = "bottomleft" )
    })
    output$map_comp2 <- renderLeaflet({
      leaflet()%>%
      addProviderTiles("CartoDB")%>%
      addPolygons(
      data = violations[[input$type_comp]][[input$time2]],
      weight = 0.5,
      color = "black",
      stroke=TRUE ,
      opacity = 1 ,
      fillColor = ~nc_pal(Total),
      label = ~paste0 ('Total Violations : ' , Total),
      group = '2022',
      highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
      )%>%
      addLegend( pal=nc_pal, values= violations[[input$type]][[input$time2]]$Total, opacity=0.9, title = "Count of Total Violation", position = "bottomleft" )
    
    
    })
}

shinyApp(ui,server)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCnJ1bnRpbWU6IHNoaW55Ci0tLQoKCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2VvanNvbmlvKQpsaWJyYXJ5KGJyb29tKQpsaWJyYXJ5KHNmKQpsaWJyYXJ5KG9zbWRhdGEpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShkcGx5cikKbGlicmFyeShnZ25ld3NjYWxlKQpsaWJyYXJ5KFJTb2NyYXRhKQpsaWJyYXJ5KHJzdHVkaW9hcGkpCmxpYnJhcnkobGVhZmxldCkKbGlicmFyeShzaGlueSkKbGlicmFyeShic2xpYikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCiNzZXQgd29ya2luZyBkaXJlY3RvcnkKY3VycmVudF9wYXRoIDwtIHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgKc2V0d2QoZGlybmFtZShjdXJyZW50X3BhdGgpKQpnZXR3ZCgpCmBgYAoKCmBgYHtyfQojIyMjIExvYWQgdGhlIFJlc3RhdXJhbnQgZGF0YXNldApkYXRhID0gIHJlYWQuc29jcmF0YSgKICAiaHR0cHM6Ly9kYXRhLmNpdHlvZm5ld3lvcmsudXMvcmVzb3VyY2UvNDNubi1wbjhqLmpzb24iLAogIGFwcF90b2tlbiA9ICJ6VFJlaHAxODk3U1F0cFl0QmlJT1VNZlI0IgopCgpkYXRhJHllYXIgPC0gZm9ybWF0KGRhdGEkaW5zcGVjdGlvbl9kYXRlLCIlWSIpICMjIEV4dHJhY3QgWWVhcnMKCiMjI2ZpbHRlciB0aGUgZGF0YXNldApkZiA9CiAgZGF0YSAlPiUKICBmaWx0ZXIoZGF0YSR5ZWFyID49IDIwMTkgJiB6aXBjb2RlIT0iIiAmIGRiYSAhPSAiIikgJT4lCiAgbXV0YXRlKGdyYWRlPXJlcGxhY2UoZ3JhZGUsIGdyYWRlID09ICIiLCBOQSkpCgpoZWFkKGRmKQpgYGAKCgpgYGB7cn0KIyBSZWFkIHRoZSBnZW9qc29uIGZpbGUgY29udGFpbmluZyBTcGF0aWFsIEluZm8Kc3BkZl9maWxlIDwtIGdlb2pzb25fcmVhZCgiLi4vZGF0YS96aXBfY29kZV8wNDAxMTQuZ2VvanNvbiIsIHdoYXQgPSAic3AiKQoKc3RhdHNfZGYgPSBzcGRmX2ZpbGVAZGF0YQoKIyBDb252ZXJ0IGl0IHRvIGEgc3BhdGlhbCBkYXRhIGZyYW1lLCB3aXRoIHppcCBjb2RlIGFzIGluZGV4CnNwZGZfZGF0YSA8LSB0aWR5KCAgc3BkZl9maWxlLAogIHJlZ2lvbj0iWklQQ09ERSIgICMgVXNlIFpJUENPREUgdmFyaWFibGUgYXMgaW5kZXgsIHRoZSBpbmRleCB3aWxsIGJlIG5hbWVkICJpZCIKKQpgYGAKCgojIyMgU2VjdGlvbiBJSTogaW50ZXJhY3RpdmUgbWFwCgpgYGB7cn0KCiMjIyMjIE5vbmUgSW50ZXJhY3RpdmUgbWFwIChQb3B1bGF0aW9uIGJ5IHJlZ2lvbikKZ2dwbG90KCkgKwogIGdlb21fcG9seWdvbihkYXRhPXNwZGZfZGF0YSAlPiUKICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4oc3RhdHNfZGYsIGMoImlkIj0iWklQQ09ERSIpKSwKICAgICAgICAgICAgICAgYWVzKHg9bG9uZywKICAgICAgICAgICAgICAgICAgIHk9bGF0LAogICAgICAgICAgICAgICAgICAgZ3JvdXA9Z3JvdXAsCiAgICAgICAgICAgICAgICAgICBmaWxsPVBPUFVMQVRJT04pLAogICAgICAgICAgICAgICBjb2xvcj0id2hpdGUiLAogICAgICAgICAgICAgICBzaXplPS4yKSArCiAgdGhlbWVfdm9pZCgpICsKICBjb29yZF9tYXAoKSArCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIocGFsZXR0ZSA9ICJZbEduQnUiLCBkaXJlY3Rpb24gPSAxKSArCiAgbGFicyh0aXRsZT0iUG9wdWxhdGlvbiBpbiBOZXcgWW9yayBDaXR5IiwKICAgICAgIHN1YnRpdGxlPSJOZWlnaGJvcmhvb2RzIGFyZSBmaWxsZWQgYnkgcG9wdWxhdGlvbiIsCiAgICAgICBmaWxsPSJQb3B1bGF0aW9uIikKYGBgCgoKCmBgYHtyfQoKIyMjIyBOdW1iZXIgb2YgcmVzdGF1cmFudCBwZXIgWklQQ09ERQpOdW1fUmVzdF9Db2RlID0KICBkZiU+JQogIGdyb3VwX2J5KHppcGNvZGUsIGRiYSwgbGF0aXR1ZGUsIGxvbmdpdHVkZSklPiUKICBjb3VudCgpICU+JQogIGdyb3VwX2J5KHppcGNvZGUpJT4lCiAgY291bnQoKQoKQ3JpdGljYWxfMjAxOV9ieV9Db2RlID0gCiAgZGYlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDE5KSU+JQogIGdyb3VwX2J5KHppcGNvZGUpJT4lCiAgc3VtbWFyaXplKFRvdGFsID0gbigpKQogIApDcml0aWNhbF8yMDIwX2J5X0NvZGUgPSAKICBkZiU+JQogIGZpbHRlcih5ZWFyID09IDIwMjApJT4lCiAgZ3JvdXBfYnkoemlwY29kZSklPiUKICBzdW1tYXJpemUoVG90YWwgPSBuKCkpCiAgCkNyaXRpY2FsXzIwMjFfYnlfQ29kZSA9IAogIGRmJT4lCiAgZmlsdGVyKHllYXIgPT0gMjAyMSklPiUKICBncm91cF9ieSh6aXBjb2RlKSU+JQogIHN1bW1hcml6ZShUb3RhbCA9IG4oKSkKICAKQ3JpdGljYWxfMjAyMl9ieV9Db2RlID0gCiAgZGYlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDIyKSU+JQogIGdyb3VwX2J5KHppcGNvZGUpJT4lCiAgc3VtbWFyaXplKFRvdGFsID0gbigpKQoKCkNyaXRpY2FsX3NwZGZfZmlsZV8yMDIyID0gc3BkZl9maWxlCkNyaXRpY2FsX3NwZGZfZmlsZV8yMDIyQGRhdGEgPQpDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMkBkYXRhICU+JQogICAgICAgICAgICAgICAgIGxlZnRfam9pbihDcml0aWNhbF8yMDIyX2J5X0NvZGUsIGMoIlpJUENPREUiPSJ6aXBjb2RlIikpCgoKCkNyaXRpY2FsX3NwZGZfZmlsZV8yMDE5ID0gc3BkZl9maWxlCkNyaXRpY2FsX3NwZGZfZmlsZV8yMDE5QGRhdGEgPQpDcml0aWNhbF9zcGRmX2ZpbGVfMjAxOUBkYXRhICU+JQogICAgICAgICAgICAgICAgIGxlZnRfam9pbihDcml0aWNhbF8yMDE5X2J5X0NvZGUsIGMoIlpJUENPREUiPSJ6aXBjb2RlIikpCgoKQ3JpdGljYWxfc3BkZl9maWxlXzIwMjAgPSBzcGRmX2ZpbGUKQ3JpdGljYWxfc3BkZl9maWxlXzIwMjBAZGF0YSA9CkNyaXRpY2FsX3NwZGZfZmlsZV8yMDIwQGRhdGEgJT4lCiAgICAgICAgICAgICAgICAgbGVmdF9qb2luKENyaXRpY2FsXzIwMjBfYnlfQ29kZSwgYygiWklQQ09ERSI9InppcGNvZGUiKSkKCkNyaXRpY2FsX3NwZGZfZmlsZV8yMDIxID0gc3BkZl9maWxlCkNyaXRpY2FsX3NwZGZfZmlsZV8yMDIxQGRhdGEgPQpDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMUBkYXRhICU+JQogICAgICAgICAgICAgICAgIGxlZnRfam9pbihDcml0aWNhbF8yMDIxX2J5X0NvZGUsIGMoIlpJUENPREUiPSJ6aXBjb2RlIikpCgoKY3JpdGljYWxfdmlvbGF0aW9ucyA9IGxpc3QoQ3JpdGljYWxfc3BkZl9maWxlXzIwMTksQ3JpdGljYWxfc3BkZl9maWxlXzIwMjAsQ3JpdGljYWxfc3BkZl9maWxlXzIwMjEsQ3JpdGljYWxfc3BkZl9maWxlXzIwMjIpCm5hbWVzKGNyaXRpY2FsX3Zpb2xhdGlvbnMpIDwtIGMoIjIwMTkiLCIyMDIwIiwiMjAyMSIsIjIwMjIiKQoKCgpuY19wYWw9IGNvbG9yTnVtZXJpYyhwYWxldHRlPSJZbE9yQnIiLCBkb21haW49IENyaXRpY2FsX3NwZGZfZmlsZV8yMDE5QGRhdGEkVG90YWwsIG5hLmNvbG9yID0gJ3RyYW5zcGFyZW50JykKCgoKbGVhZmxldCgpJT4lCiAgYWRkUHJvdmlkZXJUaWxlcygiQ2FydG9EQiIpJT4lCiAgIyMjIyBGaXJzdCBMYXllciBvZiBQb2x5R29ucwogIGFkZFBvbHlnb25zKAogICAgZGF0YSA9IENyaXRpY2FsX3NwZGZfZmlsZV8yMDIyICwKICAgIHdlaWdodCA9IDAuNSwKICAgIGNvbG9yID0gImJsYWNrIiwKICAgIHN0cm9rZT1UUlVFICwKICAgIG9wYWNpdHkgPSAxICwKICAgIGZpbGxDb2xvciA9IH5uY19wYWwoVG90YWwpLAogICAgbGFiZWwgPSB+cGFzdGUwICgnVG90YWwgQ3JpdGljYWwgVmlvbGF0aW9uIDogJyAsIFRvdGFsKSwKICAgIGdyb3VwID0gJzIwMjInLAogICAgaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0T3B0aW9ucyh3ZWlnaHQgID0gMywgY29sb3IgPSAicmVkIiwgYnJpbmdUb0Zyb250ID0gIFQpCiAgICApICU+JQogIAogICMjIyMgU2Vjb25kIExheWVyIG9mIFBvbHlHb25zCiAgICBhZGRQb2x5Z29ucygKICAgIGRhdGEgPSBDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMSAsCiAgICB3ZWlnaHQgPSAwLjUsCiAgICBjb2xvciA9ICJibGFjayIsCiAgICBzdHJva2U9VFJVRSAsCiAgICBvcGFjaXR5ID0gMSAsCiAgICBmaWxsQ29sb3IgPSB+bmNfcGFsKFRvdGFsKSwKICAgIAogICAgbGFiZWwgPX5wYXN0ZTAgKCdUb3RhbCBDcml0aWNhbCBWaW9sYXRpb24gOiAnICwgVG90YWwpLAogICAgZ3JvdXAgPSAnMjAyMScsCiAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCAgPSAzLCBjb2xvciA9ICJyZWQiLCBicmluZ1RvRnJvbnQgPSAgVCkKICAgICkgJT4lCiAgICBhZGRMYXllcnNDb250cm9sKG92ZXJsYXlHcm91cHMgPSBjKCIyMDIyIiwgIjIwMjEiKSklPiUKICAKICAKICAKICAgIGFkZExlZ2VuZCggcGFsPW5jX3BhbCwgdmFsdWVzPSBDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMiRUb3RhbCwgb3BhY2l0eT0wLjksIHRpdGxlID0gIkNyaXRpY2FsIiwgcG9zaXRpb24gPSAiYm90dG9tbGVmdCIgKQogICAgCmBgYAoKCgpgYGB7cn0KCgoKVG90YWxfdmlvbGF0aW9uID0gCiAgZGYlPiUKICBncm91cF9ieSh6aXBjb2RlKSU+JQogIHN1bW1hcml6ZShUb3RhbCA9IG4oKSkKCgpUb3RhbF8yMDE5X2J5X0NvZGUgPSAKICBkZiU+JQogIGZpbHRlcih5ZWFyID09IDIwMTkpJT4lCiAgZ3JvdXBfYnkoemlwY29kZSklPiUKICBzdW1tYXJpemUoVG90YWwgPSBuKCkpCiAgClRvdGFsXzIwMjBfYnlfQ29kZSA9IAogIGRmJT4lCiAgZmlsdGVyKHllYXIgPT0gMjAyMCklPiUKICBncm91cF9ieSh6aXBjb2RlKSU+JQogIHN1bW1hcml6ZShUb3RhbCA9IG4oKSkKICAKVG90YWxfMjAyMV9ieV9Db2RlID0gCiAgZGYlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDIxKSU+JQogIGdyb3VwX2J5KHppcGNvZGUpJT4lCiAgc3VtbWFyaXplKFRvdGFsID0gbigpKQogIApUb3RhbF8yMDIyX2J5X0NvZGUgPSAKICBkZiU+JQogIGZpbHRlcih5ZWFyID09IDIwMjIpJT4lCiAgZ3JvdXBfYnkoemlwY29kZSklPiUKICBzdW1tYXJpemUoVG90YWwgPSBuKCkpCgoKCgoKIyMjIyMgSm9pbiBkYXRhc2V0cwpUb3RhbF9zcGRmX2ZpbGVfMjAyMiA9IHNwZGZfZmlsZQpUb3RhbF9zcGRmX2ZpbGVfMjAyMkBkYXRhID0KVG90YWxfc3BkZl9maWxlXzIwMjJAZGF0YSAlPiUKICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4oVG90YWxfMjAyMl9ieV9Db2RlLCBjKCJaSVBDT0RFIj0iemlwY29kZSIpKQoKCgpUb3RhbF9zcGRmX2ZpbGVfMjAxOSA9IHNwZGZfZmlsZQpUb3RhbF9zcGRmX2ZpbGVfMjAxOUBkYXRhID0KVG90YWxfc3BkZl9maWxlXzIwMTlAZGF0YSAlPiUKICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4oVG90YWxfMjAxOV9ieV9Db2RlLCBjKCJaSVBDT0RFIj0iemlwY29kZSIpKQoKClRvdGFsX3NwZGZfZmlsZV8yMDIwID0gc3BkZl9maWxlClRvdGFsX3NwZGZfZmlsZV8yMDIwQGRhdGEgPQpUb3RhbF9zcGRmX2ZpbGVfMjAyMEBkYXRhICU+JQogICAgICAgICAgICAgICAgIGxlZnRfam9pbihUb3RhbF8yMDIwX2J5X0NvZGUsIGMoIlpJUENPREUiPSJ6aXBjb2RlIikpCgpUb3RhbF9zcGRmX2ZpbGVfMjAyMSA9IHNwZGZfZmlsZQpUb3RhbF9zcGRmX2ZpbGVfMjAyMUBkYXRhID0KVG90YWxfc3BkZl9maWxlXzIwMjFAZGF0YSAlPiUKICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4oVG90YWxfMjAyMV9ieV9Db2RlLCBjKCJaSVBDT0RFIj0iemlwY29kZSIpKQoKCnRvdGFsX3Zpb2xhdGlvbiA8LSBsaXN0KFRvdGFsX3NwZGZfZmlsZV8yMDE5LFRvdGFsX3NwZGZfZmlsZV8yMDIwLFRvdGFsX3NwZGZfZmlsZV8yMDIxLFRvdGFsX3NwZGZfZmlsZV8yMDIyKQpuYW1lcyh0b3RhbF92aW9sYXRpb24pIDwtIGMoIjIwMTkiLCIyMDIwIiwiMjAyMSIsIjIwMjIiKQoKCiMjIyMjIGNvbG9ycwpuY19wYWw9IGNvbG9yTnVtZXJpYyhwYWxldHRlPSJZbE9yQnIiLCBkb21haW49IFRvdGFsX3NwZGZfZmlsZV8yMDE5QGRhdGEkVG90YWwsIG5hLmNvbG9yID0gJ3RyYW5zcGFyZW50JykKCgpsZWFmbGV0KCklPiUKICBhZGRQcm92aWRlclRpbGVzKCJDYXJ0b0RCIiklPiUKICAjIyMjIEZpcnN0IExheWVyIG9mIFBvbHlHb25zCiAgYWRkUG9seWdvbnMoCiAgICBkYXRhID0gVG90YWxfc3BkZl9maWxlXzIwMjIgLAogICAgd2VpZ2h0ID0gMC41LAogICAgY29sb3IgPSAiYmxhY2siLAogICAgc3Ryb2tlPVRSVUUgLAogICAgb3BhY2l0eSA9IDEgLAogICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICBsYWJlbCA9IH5wYXN0ZTAgKCdUb3RhbCBDcml0aWNhbCBWaW9sYXRpb24gOiAnICwgVG90YWwpLAogICAgZ3JvdXAgPSAnMjAyMicsCiAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCAgPSAzLCBjb2xvciA9ICJyZWQiLCBicmluZ1RvRnJvbnQgPSAgVCkKICAgICkgJT4lCiAgCiAgIyMjIyBTZWNvbmQgTGF5ZXIgb2YgUG9seUdvbnMKICAgIGFkZFBvbHlnb25zKAogICAgZGF0YSA9IFRvdGFsX3NwZGZfZmlsZV8yMDIxICwKICAgIHdlaWdodCA9IDAuNSwKICAgIGNvbG9yID0gImJsYWNrIiwKICAgIHN0cm9rZT1UUlVFICwKICAgIG9wYWNpdHkgPSAxICwKICAgIGZpbGxDb2xvciA9IH5uY19wYWwoVG90YWwpLAogICAgbGFiZWwgPX5wYXN0ZTAgKCdUb3RhbCAgVmlvbGF0aW9uIDogJyAsIFRvdGFsKSwKICAgIGdyb3VwID0gJzIwMjEnLAogICAgaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0T3B0aW9ucyh3ZWlnaHQgID0gMywgY29sb3IgPSAicmVkIiwgYnJpbmdUb0Zyb250ID0gIFQpCiAgICApICU+JQogICAgYWRkTGF5ZXJzQ29udHJvbChvdmVybGF5R3JvdXBzID0gYygiMjAyMiIsICIyMDIxIikpJT4lCiAgCiAgIyMjIyNUaGlyZCBsYXllcgogICAgYWRkUG9seWdvbnMoCiAgICBkYXRhID0gVG90YWxfc3BkZl9maWxlXzIwMjAgLAogICAgd2VpZ2h0ID0gMC41LAogICAgY29sb3IgPSAiYmxhY2siLAogICAgc3Ryb2tlPVRSVUUgLAogICAgb3BhY2l0eSA9IDEgLAogICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICAKICAgIGxhYmVsID1+cGFzdGUwICgnVG90YWwgIFZpb2xhdGlvbiA6ICcgLCBUb3RhbCksCiAgICBncm91cCA9ICcyMDIwJywKICAgIGhpZ2hsaWdodCA9IGhpZ2hsaWdodE9wdGlvbnMod2VpZ2h0ICA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9ICBUKQogICAgKSAlPiUKICAKICAjIyMjRm91cnRoCiAgICBhZGRQb2x5Z29ucygKICAgIGRhdGEgPSBUb3RhbF9zcGRmX2ZpbGVfMjAxOSAsCiAgICB3ZWlnaHQgPSAwLjUsCiAgICBjb2xvciA9ICJibGFjayIsCiAgICBzdHJva2U9VFJVRSAsCiAgICBvcGFjaXR5ID0gMSAsCiAgICBmaWxsQ29sb3IgPSB+bmNfcGFsKFRvdGFsKSwKICAgIGxhYmVsID1+cGFzdGUwICgnVG90YWwgIFZpb2xhdGlvbiA6ICcgLCBUb3RhbCksCiAgICBncm91cCA9ICcyMDE5JywKICAgIGhpZ2hsaWdodCA9IGhpZ2hsaWdodE9wdGlvbnMod2VpZ2h0ICA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9ICBUKQogICAgKSAlPiUKICAgCiAgCiAgICBhZGRMYXllcnNDb250cm9sKG92ZXJsYXlHcm91cHMgPSBjKCIyMDIyIiwgIjIwMjEiLCcyMDIwJywgJzIwMTknKSklPiUKICAgIGFkZExlZ2VuZCggcGFsPW5jX3BhbCwgdmFsdWVzPSBUb3RhbF9zcGRmX2ZpbGVfMjAyMiRUb3RhbCwgb3BhY2l0eT0wLjksIHRpdGxlID0gIkNvdW50IG9mIFRvdGFsIFZpb2xhdGlvbiIsIHBvc2l0aW9uID0gImJvdHRvbWxlZnQiICkKCnZpb2xhdGlvbnMgPC0gbGlzdCh0b3RhbF92aW9sYXRpb24sY3JpdGljYWxfdmlvbGF0aW9ucykKbmFtZXModmlvbGF0aW9ucykgPC0gYygiTnVtYmVyIG9mIFRvdGFsIFZpb2xhdGlvbnMiLCJOdW1iZXIgb2YgQ3JpdGFsIFZpb2xhdGlvbnMiKQoKYGBgCgoKIyMjIyByZXN0YXVyYW50IGluZm8KYGBge3J9CiBzY29yZSA8LSBkYXRhICU+JQogIGZpbHRlcighaXMubmEoc2NvcmUpKSU+JQogIGdyb3VwX2J5KGRiYSklPiUKICBmaWx0ZXIoaW5zcGVjdGlvbl9kYXRlPT1tYXgoaW5zcGVjdGlvbl9kYXRlKSklPiUKICBzZWxlY3QoZGJhLHBob25lLHNjb3JlLGxvbmdpdHVkZSxsYXRpdHVkZSxjdWlzaW5lX2Rlc2NyaXB0aW9uLGluc3BlY3Rpb25fZGF0ZSxjcml0aWNhbF9mbGFnKSU+JQogIGRpc3RpbmN0KGRiYSwgLmtlZXBfYWxsID0gVCklPiUKICB1bmdyb3VwKCkKc2NvcmUkbGF0aXR1ZGUgPC0gYXMubnVtZXJpYyhzY29yZSRsYXRpdHVkZSkKc2NvcmUkbG9uZ2l0dWRlIDwtIGFzLm51bWVyaWMoc2NvcmUkbG9uZ2l0dWRlKQpzY29yZSR5ZWFyIDwtIGZvcm1hdChzY29yZSRpbnNwZWN0aW9uX2RhdGUsIiVZIikKc2NvcmVfeWVhciA8LSBsaXN0KCkKZm9yICh5IGluIGMoIjIwMTkiLCIyMDIwIiwiMjAyMSIsIjIwMjIiKSl7CiAgc2NvcmVfeWVhclt5XSA8LSBzY29yZVtzY29yZSR5ZWFyPT15LF0KfQpzY29yZTEgPC0gc2NvcmVbMToxMCxdCmxlYWZsZXQoKSU+JQogIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIiKSU+JQogIGFkZE1hcmtlcnMoCiAgICBsYXQgPSBzY29yZV95ZWFyWyRsYXRpdHVkZSwKICAgIGxuZyA9IHNjb3JlMSRsb25naXR1ZGUsCiAgICBwb3B1cCA9IHBhc3RlKHNjb3JlMSRkYmEsIlNjb3JlOiIsIHNjb3JlMSRzY29yZSwKICAgICAgICAgICAgICAgICAgICI8YnI+Q3Vpc2luZToiLCBzY29yZTEkY3Vpc2luZV9kZXNjcmlwdGlvbiwKICAgICAgICAgICAgICAgICAgICI8YnI+VXBkYXRlZCBUaW1lOiIsIHNjb3JlMSRpbnNwZWN0aW9uX2RhdGUsCiAgICAgICAgICAgICAgICAgICAiPGJyPkNyaXRpY2FsIEZsYWc6Iiwgc2NvcmUxJGNyaXRpY2FsX2ZsYWcpCiAgKQoKCmBgYAoKCgoKIyMjIFNlY3Rpb24gSUlJOiBSc2hpbnkgQXBwCgoKYGBge3J9Cm5jX3BhbD0gY29sb3JOdW1lcmljKHBhbGV0dGU9IllsT3JCciIsIGRvbWFpbj0gVG90YWxfc3BkZl9maWxlXzIwMjJAZGF0YSRUb3RhbCxuYS5jb2xvciA9ICd0cmFuc3BhcmVudCcpCnVpIDwtIG5hdmJhclBhZ2UoCiB0aGVtZSA9IGJzX3RoZW1lKGJvb3Rzd2F0Y2ggPSAibGl0ZXJhIiksCiAgIkZvb2QgSW5zcGVjdGF0aW9uIiwKICB0YWJQYW5lbCgiSW50cm9kdWN0aW9uIiksCiAgdGFiUGFuZWwoIlN0YXRpYyBQbG90cyIpLAogCiAgbmF2YmFyTWVudSgiSW50ZXJhY3RpdmUgUGxvdHMiLAogICAgICAgICAgICAgdGFiUGFuZWwoIkludGVyYWN0aXZlIE1hcCIsCiAgICAgICAgICAgZmx1aWRSb3coY29sdW1uKDYsc2VsZWN0SW5wdXQoInR5cGUiLCJUeXBlIG9mIFZpb2xhdGlvbnM6IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiTnVtYmVyIG9mIFRvdGFsIFZpb2xhdGlvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOdW1iZXIgb2YgQ3JpdGFsIFZpb2xhdGlvbnMiKSkpLAogICAgICAgICAgICAgICAgICAgIGNvbHVtbig2LHNlbGVjdElucHV0KCJ0aW1lIiwiWWVhcjoiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCIyMDE5IiwiMjAyMCIsIjIwMjEiLCIyMDIyIikpKSksCiAgICAgICAgICBmbHVpZFJvdyhsZWFmbGV0T3V0cHV0KCJtYXAiLGhlaWdodCA9IDEwMDApKSksCiAgICAgICAgICAKICAgICAgICAgIAogICAgICAgICAgICB0YWJQYW5lbCgiQ29tcGFyaXNvbiBiZXR3ZWVuIFllYXJzIiwKICAgICAgICAgICAgICAgICAgICAgIGZsdWlkUm93KGNvbHVtbig0LHNlbGVjdElucHV0KCJ0eXBlX2NvbXAiLCJUeXBlIG9mIFZpb2xhdGlvbnM6IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiTnVtYmVyIG9mIFRvdGFsIFZpb2xhdGlvbnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOdW1iZXIgb2YgQ3JpdGFsIFZpb2xhdGlvbnMiKSkpLAogICAgICAgICAgICAgICAgICAgIGNvbHVtbig0LHNlbGVjdElucHV0KCJ0aW1lMSIsIlllYXI6IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiMjAxOSIsIjIwMjAiLCIyMDIxIiwiMjAyMiIpKSksCiAgICAgICAgICAgICAgICAgICAgY29sdW1uKDQsc2VsZWN0SW5wdXQoInRpbWUyIiwiWWVhcjoiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKCIyMDE5IiwiMjAyMCIsIjIwMjEiLCIyMDIyIiksc2VsZWN0ZWQgPSAiMjAyMCIpKSksCiAgICAgICAgICAgICAgICAgICAgZmx1aWRSb3coY29sdW1uKDYsbGVhZmxldE91dHB1dCgibWFwX2NvbXAxIixoZWlnaHQ9NjAwKSksIGNvbHVtbig2LGxlYWZsZXRPdXRwdXQoIm1hcF9jb21wMiIsaGVpZ2h0PTYwMCkpKSkpLAogCiAgdGFiUGFuZWwoIlJlZmVyZW5jZSIpCikKc2VydmVyIDwtIGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQsc2Vzc2lvbil7CiAgI2ludGVyYWN0aXZlIG1hcAogIG91dHB1dCRtYXAgPC0gcmVuZGVyTGVhZmxldCh7CiAgICBsZWFmbGV0KCklPiUKICBhZGRQcm92aWRlclRpbGVzKCJDYXJ0b0RCIiklPiUKICAjIyMjIEZpcnN0IExheWVyIG9mIFBvbHlHb25zCiAgYWRkUG9seWdvbnMoCiAgICBkYXRhID0gdmlvbGF0aW9uc1tbaW5wdXQkdHlwZV1dW1tpbnB1dCR0aW1lXV0sCiAgICB3ZWlnaHQgPSAwLjUsCiAgICBjb2xvciA9ICJibGFjayIsCiAgICBzdHJva2U9VFJVRSAsCiAgICBvcGFjaXR5ID0gMSAsCiAgICBmaWxsQ29sb3IgPSB+bmNfcGFsKFRvdGFsKSwKICAgIGxhYmVsID0gfnBhc3RlMCAoJ1RvdGFsIFZpb2xhdGlvbnMgOiAnICwgVG90YWwpLAogICAgZ3JvdXAgPSAnMjAyMicsCiAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCAgPSAzLCBjb2xvciA9ICJyZWQiLCBicmluZ1RvRnJvbnQgPSAgVCkKICAgICklPiUKICAgIGFkZExlZ2VuZCggcGFsPW5jX3BhbCwgdmFsdWVzPSB2aW9sYXRpb25zW1tpbnB1dCR0eXBlXV1bW2lucHV0JHRpbWVdXSRUb3RhbCwgb3BhY2l0eT0wLjksIHRpdGxlID0gIkNvdW50IG9mIFRvdGFsIFZpb2xhdGlvbiIsIHBvc2l0aW9uID0gImJvdHRvbWxlZnQiICkKICAgIH0pCiAgCiAgCiAgCiAgCiNpbnRlcmFjdGl2ZSBtYXAgY29tcGFyZWQgYnkgeWVhciAgCiAgICBvdXRwdXQkbWFwX2NvbXAxIDwtIHJlbmRlckxlYWZsZXQoewogICAgICBsZWFmbGV0KCklPiUKICAgICAgYWRkUHJvdmlkZXJUaWxlcygiQ2FydG9EQiIpJT4lCiAgICAgIGFkZFBvbHlnb25zKAogICAgICBkYXRhID0gdmlvbGF0aW9uc1tbaW5wdXQkdHlwZV9jb21wXV1bW2lucHV0JHRpbWUxXV0sCiAgICAgIHdlaWdodCA9IDAuNSwKICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICBzdHJva2U9VFJVRSAsCiAgICAgIG9wYWNpdHkgPSAxICwKICAgICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICAgIGxhYmVsID0gfnBhc3RlMCAoJ1RvdGFsIFZpb2xhdGlvbnMgOiAnICwgVG90YWwpLAogICAgICBncm91cCA9ICcyMDIyJywKICAgICAgaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0T3B0aW9ucyh3ZWlnaHQgID0gMywgY29sb3IgPSAicmVkIiwgYnJpbmdUb0Zyb250ID0gIFQpCiAgICAgICklPiUKICAgICAgYWRkTGVnZW5kKCBwYWw9bmNfcGFsLCB2YWx1ZXM9IHZpb2xhdGlvbnNbW2lucHV0JHR5cGVdXVtbaW5wdXQkdGltZTFdXSRUb3RhbCwgb3BhY2l0eT0wLjksIHRpdGxlID0gIkNvdW50IG9mIFRvdGFsIFZpb2xhdGlvbiIsIHBvc2l0aW9uID0gImJvdHRvbWxlZnQiICkKICAgIH0pCiAgICBvdXRwdXQkbWFwX2NvbXAyIDwtIHJlbmRlckxlYWZsZXQoewogICAgICBsZWFmbGV0KCklPiUKICAgICAgYWRkUHJvdmlkZXJUaWxlcygiQ2FydG9EQiIpJT4lCiAgICAgIGFkZFBvbHlnb25zKAogICAgICBkYXRhID0gdmlvbGF0aW9uc1tbaW5wdXQkdHlwZV9jb21wXV1bW2lucHV0JHRpbWUyXV0sCiAgICAgIHdlaWdodCA9IDAuNSwKICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICBzdHJva2U9VFJVRSAsCiAgICAgIG9wYWNpdHkgPSAxICwKICAgICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICAgIGxhYmVsID0gfnBhc3RlMCAoJ1RvdGFsIFZpb2xhdGlvbnMgOiAnICwgVG90YWwpLAogICAgICBncm91cCA9ICcyMDIyJywKICAgICAgaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0T3B0aW9ucyh3ZWlnaHQgID0gMywgY29sb3IgPSAicmVkIiwgYnJpbmdUb0Zyb250ID0gIFQpCiAgICAgICklPiUKICAgICAgYWRkTGVnZW5kKCBwYWw9bmNfcGFsLCB2YWx1ZXM9IHZpb2xhdGlvbnNbW2lucHV0JHR5cGVdXVtbaW5wdXQkdGltZTJdXSRUb3RhbCwgb3BhY2l0eT0wLjksIHRpdGxlID0gIkNvdW50IG9mIFRvdGFsIFZpb2xhdGlvbiIsIHBvc2l0aW9uID0gImJvdHRvbWxlZnQiICkKICAgIAogICAgCiAgICB9KQp9CgpzaGlueUFwcCh1aSxzZXJ2ZXIpCgpgYGA=